package com.zhuchang.self_rpc.test.democoding;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @Author RenPu
 * @Date 2022/2/7 17:51
 * @Version 1.0
 * @Description: 服务端代码的编写-NIO模拟实现聊天IM
 **/
public class GroupChatServer {

    //定义选择器
    private Selector selector;

    //定义ServerSocketChannel
    private ServerSocketChannel serverSocketChannel;

    //定义端口号
    private static final int port=6667;

    public GroupChatServer() throws IOException {
        this.selector=Selector.open();   //获取选择器
        this.serverSocketChannel=ServerSocketChannel.open(); //获取ServerSocketChannel
        this.serverSocketChannel.bind(new InetSocketAddress(port));  //绑定服务器端口
        this.serverSocketChannel.configureBlocking(false);        //设置模式为非阻塞IO
        this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //将ServerSocketChannel注册到选择器上
    }


    /**
     * 服务端的监听方法
     */
    public void listen() {
        try{
            //1：从选择器获取发生变化的事件
            while (true){
                int count = selector.select();  //从选择器中获取，可以获取事件的个数
                if(count>0){
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        SelectionKey next = iterator.next();   //根据SelectionKey获取到对应的SocketChannel
                        if(next.isAcceptable()){
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            socketChannel.configureBlocking(false);   //设置为非阻塞
                            /**
                             * int OP_ACCEPT：有新的网络连接可以 accept，值为 16
                             * int OP_CONNECT：代表连接已经建立，值为 8
                             * int OP_READ：代表读操作，值为 1
                             * int OP_WRITE：代表写操作，值为 4
                             */
                            socketChannel.register(selector,SelectionKey.OP_READ);
//                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//                        socketChannel.write(byteBuffer);
                            System.out.println(socketChannel.getRemoteAddress()+": 已上线");  //打印客户端的远程地址
                        }
                        if(next.isReadable()){
                            readData(next);      // next为可读取的状态时，直接将socketChannel的数据读取到byteBuffer中
                        }

                        //当前的 key 删除，防止重复处理
                        iterator.remove();
                    }

                }else {
                    System.out.println("等待中.......");
                }

            }

            //2：迭代遍历从选择器获取的值，判断是否为已经连接

            //3：通过ServerSocketChannel获取客户端对象（socketChannel）

            //4: 设置客户端也为非阻塞IO模式
        }catch (IOException ioException){

        }finally {
            //发生异常进行处理
        }



    }


    /**
     * 从SocketChannel对象读取数据
     * @param selectionKey
     */
    private void readData(SelectionKey selectionKey) {

        SocketChannel channel=null;
        try {
            //通过对应的selectionKey，获取对应的SocketChannel
            channel = (SocketChannel) selectionKey.channel();

            //获取ByteBuffer对象
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            //从channel读取数据下标的值
            int read = channel.read(buffer);
            if(read>0){
                String content = new String(buffer.array());
                System.out.println("from 客户端 :"+content);   //输出在服务器的控制台

                //转发消息
                sendInfoToOtherClients(content,channel);
            }

        } catch (IOException ioException) {
            try {
                System.out.println(channel.getRemoteAddress()+"离线了......");
                selectionKey.channel();//取消注册
                channel.close(); //关闭通道
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }


    /**
     * 将客户端1消息，转发给客户端2进行消息发送
     * @param content
     * @param self
     */
    private void sendInfoToOtherClients(String content, SocketChannel self) {

        System.out.println("服务器转发消息中...");
        //遍历所有注册到 selector 上的 SocketChannel,并排除 self
        for (SelectionKey key : selector.keys()) {
            //通过 key 取出对应的 SocketChannel
            Channel targetChannel = key.channel();
            //targetChannel是属于SocketChannel子类，同时排除自己
            if (targetChannel instanceof SocketChannel && targetChannel != self) {
                //转型
                SocketChannel dest = (SocketChannel) targetChannel;
                //将 msg 存储到 buffer
                ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());
                //将 buffer 的数据写入通道
                try {
                    dest.write(buffer);
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }


    }


    public static void main(String[] args) throws IOException {
        //创建服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }


}
